#include "transition.h"

#include "arc.h"
#include "expression.h"
#include "place.h"
#include "wfnet.h"

#include <bool.h>
#include <directedgraph.h>

using namespace wfnet;

Transition::~Transition()
{

}

nid Transition::getId() const
{
    return m_id;
}

QString Transition::name() const
{
    return m_name;
}

IExpression* Transition::getGuard() const
{
    return m_guard;
}

BasicTime Transition::getDelay() const
{
    return m_delay;
}

void Transition::setGuard(IExpression* guard)
{
    if (Bool::NAME == guard->returns())
    {
        m_guard = dynamic_cast<Expression* >(guard);
    }
}

void Transition::setDelay(const BasicTime &time)
{
    m_delay = time;
}

QSet<const IPlace* > Transition::getInputPlaces() const
{
    QSet<const IPlace* > iPlaces;

    if (m_node != 0)
    {
        const QSet<Node* > iConnections = m_node->getIncomingConnections();
        foreach (Node* node, iConnections)
        {
            const Place* place =
                dynamic_cast<const Place *>(node->getData());

            iPlaces << place;
        }
    }

    return iPlaces;
}

QSet<const IPlace* > Transition::getOutputPlaces() const
{
    QSet<const IPlace* > oPlaces;

    if (m_node != 0)
    {
        const QSet<Node* > oConnections = m_node->getOutgoingConnections();
        foreach (Node* node, oConnections)
        {
            const Place* place =
                dynamic_cast<const Place *>(node->getData());

            oPlaces << place;
        }
    }

    return oPlaces;
}

void Transition::fire(const BasicTime& globalClock)
{
    const WfNet* net = qobject_cast<const WfNet* >(parent());

    Q_ASSERT(net != 0);

    const QSet<const Arc* > iArcs = getInputArcs(net->getGraph());

    foreach (const Arc* arc, iArcs)
    {
        IMultiset* emt = arc->eval(globalClock);
        arc->getPlace()->withdrawTokens(emt);

        delete emt;
    }

    const QSet<const Arc* > oArcs = getOutputArcs(net->getGraph());

    foreach (const Arc* arc, oArcs)
    {
        BasicTime _globalClock = globalClock;
        _globalClock.add(arc->getDelay().getTime());

        IMultiset* emt = arc->eval(_globalClock);
        arc->getPlace()->putTokens(emt);

        delete emt;
    }
}

void Transition::registerNode(Node* node)
{
    m_node = node;
}

const Node* const Transition::node() const
{
    return m_node;
}

bool Transition::check() const
{
    bool ret = true;

    if (m_guard != 0)
    {
        const ValueType* type = dynamic_cast<const ValueType* >(
            m_guard->eval());

        Q_ASSERT(type != 0); // type mismatch, should be Bool

        ret = type->getValue().toBool();

        delete type; // no longer needed
    }

    return ret;
}

Transition::Transition(WfNet *parent, nid id, QString name,
    BasicTime delay) : QObject(parent), ITransition(), INodeData(),
        m_node(0),
        m_id(id),
        m_name(name),
        m_guard(0),
        m_delay(delay)
{

}

QSet<const Arc* > Transition::getInputArcs(const DirectedGraph& graph) const
{
    QSet<const Arc* > iArcs;

    if (m_node != 0)
    {
        const QSet<Node* > iConnections = m_node->getIncomingConnections();
        foreach (Node* node, iConnections)
        {
            const Place* place =
                dynamic_cast<const Place *>(node->getData());

            const Arc* arc = dynamic_cast<const Arc* >(graph.getEdge(
                place->m_node, m_node)->getData());

            iArcs << arc;
        }
    }

    return iArcs;
}

QSet<const Arc* > Transition::getOutputArcs(const DirectedGraph& graph) const
{
    QSet<const Arc* > oArcs;

    if (m_node != 0)
    {
        const QSet<Node* > oConnections = m_node->getOutgoingConnections();
        foreach (Node* node, oConnections)
        {
            const Place* place =
                dynamic_cast<const Place *>(node->getData());

            const Arc* arc = dynamic_cast<const Arc* >(graph.getEdge(
                m_node, place->m_node)->getData());

            oArcs << arc;
        }
    }

    return oArcs;
}

